package session

import (
	"bytes"
	"encoding/base64"
	"net/http"
	"net/url"
	"strconv"
	"sync"
	"time"
)

type CookieSessionManager struct {
	data   map[string]ISession
	sid    string
	smux   sync.Mutex //互斥锁
	gcTime int64
	timer  *time.Timer
}

func NewCookieSessionManager() *CookieSessionManager {
	return &CookieSessionManager{
		data:   make(map[string]ISession),
		gcTime: -1,
	}
}

func (m *CookieSessionManager) Set(id string, session ISession) {
	m.lock()
	defer m.unlock()
	m.data[id] = session
}

func (m *CookieSessionManager) Remove(id string) {
	m.lock()
	defer m.unlock()
	delete(m.data, id)
}

func (m *CookieSessionManager) Destory() {

}

func (m *CookieSessionManager) Get(id string) (ISession, bool) {
	m.lock()
	defer m.unlock()
	i := m.data[id]
	return i, i != nil
}

//func (m *CookieSessionManager) BindSession(session ISession, w http.ResponseWriter, r *http.Request) {
//	fmt.Println("CookieSessionManager BindSession")
//	m.lock()
//	defer m.unlock()
//	cookie := http.Cookie{
//		Name: m.sid,
//		//这里是并发不安全的，但是这个方法已上锁
//		Value:    url.QueryEscape(session.GetId()), //转义特殊符号@#￥%+*-等
//		Path:     "/",
//		HttpOnly: true,
//		MaxAge:   int(m.maxAge),
//		Expires:  time.Now().Add(time.Duration(m.maxAge)),
//	}
//	http.SetCookie(w, &cookie) //设置到响应中
//	m.data[session.GetId()] = session
//}

func (m *CookieSessionManager) BindSession(bundle ...interface{}) {
	if bundle == nil || len(bundle) < 2 {
		panic("bundle must has 2 param")
	}

	session, ok := bundle[0].(ISession)
	if !ok {
		panic("first param must be server.ISession")
	}

	w, ok := bundle[1].(http.ResponseWriter)

	if !ok {
		panic("second param must be http.ResponseWriter")
	}

	m.lock()
	defer m.unlock()
	cookie := http.Cookie{
		Name: m.sid,
		//这里是并发不安全的，但是这个方法已上锁
		Value:    url.QueryEscape(session.GetId()), //转义特殊符号@#￥%+*-等
		Path:     "/",
		HttpOnly: true,
		MaxAge:   int(session.MaxAge()),
		Expires:  time.Now().Add(time.Duration(session.MaxAge())),
	}
	http.SetCookie(w, &cookie) //设置到响应中
	m.data[session.GetId()] = session

}

func (m *CookieSessionManager) CreateId(param ...string) string {
	var buffer bytes.Buffer //Buffer是一个实现了读写方法的可变大小的字节缓冲
	for _, v := range param {
		buffer.WriteString(v)
		buffer.WriteString("-")
	}
	buffer.WriteString(strconv.FormatInt(time.Now().UnixNano()/1e6, 10))
	return base64.StdEncoding.EncodeToString([]byte(buffer.String()))
}

func (m *CookieSessionManager) SetGCTime(gcTime int64) {
	m.gcTime = gcTime
	if m.gcTime < 1 {
		return
	}
	//定时回收
	if m.timer != nil {
		m.timer.Stop()
	}
	m.timer = time.AfterFunc(time.Duration(m.gcTime)*time.Second, func() { m.GC() })
}

func (m *CookieSessionManager) GC() {
	m.lock()
	defer m.unlock()
	for k, s := range m.data {
		if (s.LastAccessedTime().Unix() + s.MaxAge()) < time.Now().Unix() {
			delete(m.data, k)
		}
	}
}

func (m *CookieSessionManager) lock() {
	m.smux.Lock()
}

func (m *CookieSessionManager) unlock() {
	m.smux.Unlock()
}

type MemorySession struct {
	sid              string
	lastAccessedTime time.Time                   //最后访问时间
	data             map[interface{}]interface{} //值
	maxAge           int64                       // 最大存活 秒
}

func NewMemrorySession(sid string, maxAge int64) ISession {
	return &MemorySession{
		sid:    sid,
		maxAge: maxAge,
		data:   make(map[interface{}]interface{}),
	}
}

func (session *MemorySession) LastAccessedTime() time.Time {
	return session.lastAccessedTime
}

func (session *MemorySession) MaxAge() int64 {
	return session.maxAge
}

func (session *MemorySession) GetId() string {
	return session.sid
}

func (session *MemorySession) Set(key, value interface{}) {
	session.UpdateLastTime()
	session.data[key] = value
}

func (session *MemorySession) Get(key interface{}) (interface{}, bool) {
	session.UpdateLastTime()
	i := session.data[key]
	return i, i != nil
}

func (session *MemorySession) Remove(key interface{}) {
	session.UpdateLastTime()
	delete(session.data, key)
}

func (session *MemorySession) Destory() {
	for k := range session.data {
		delete(session.data, k)
	}
}

func (session *MemorySession) CreateId(param ...string) string {
	session.UpdateLastTime()
	var buffer bytes.Buffer //Buffer是一个实现了读写方法的可变大小的字节缓冲
	for _, v := range param {
		buffer.WriteString(v)
		buffer.WriteString("-")
	}
	buffer.WriteString(strconv.FormatInt(time.Now().UnixNano()/1e6, 10))
	return base64.StdEncoding.EncodeToString([]byte(buffer.String()))
}

func (session *MemorySession) SetGCTime(maxAge int64) {
	session.UpdateLastTime()
	session.maxAge = maxAge
}

func (session *MemorySession) UpdateLastTime() {
	session.lastAccessedTime = time.Now()
}
